package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.tbit.common.constant.CommonConstant;
import com.tbit.uqbike.constant.UserConstant;
import com.tbit.uqbike.manager.exception.BaseException;
import com.tbit.uqbike.object.business.NoticeSearch;
import com.tbit.uqbike.object.business.NoticeStatisticsSearch;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.dto.NoticeStatisticsDTO;
import com.tbit.uqbike.object.pojo.vo.NoticeVO;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.business.HolderAccountBoundService;
import com.tbit.uqbike.service.business.RoleService;
import com.tbit.uqbike.webmanager.dao.core.*;
import com.tbit.uqbike.service.business.NoticeService;
import com.tbit.utils.DateTimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.cms.PasswordRecipientId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 公告表(Notice)表服务实现类
 *
 * @author shanyong.mo
 * @since 2025-02-18 14:53:32
 */
@Slf4j
@Service("noticeService")
public class NoticeServiceImpl implements NoticeService {

    private static final ThreadLocal<List<Integer>> threadLocalRoleIdList = new ThreadLocal<>();
    private static final ThreadLocal<List<Integer>> threadLocalUserAccountIdList = new ThreadLocal<>();
    @Resource
    private NoticeDao noticeDao;
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private NoticeAppointTypeDao noticeAppointTypeDao;
    @Autowired
    private NoticeAppointPlatformDao noticeAppointPlatformDao;
    @Autowired
    private NoticeAppointAccountDao noticeAppointAccountDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private NoticeReadLogDao noticeReadLogDao;

    @Autowired
    private HolderAccountBoundService holderAccountBoundService;
    @Autowired
    private RoleService roleService;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public NoticeAll queryById(Integer id) {
        return this.noticeDao.queryById(id);
    }

    /**
     * @description: 批量查询
     * @param: idList 查询的id集合
     * @return: java.util.List<Notice>
     */
    @Override
    public List<NoticeAll> queryByIds(List<Integer> idList) {
        return this.noticeDao.queryByIds(idList);
    }


    /**
     * 分页查询
     *
     * @param noticeSearch   筛选条件
     * @return 查询结果
     */
    @Override
    public List<NoticeAll> queryByPage(NoticeSearch noticeSearch,LoginInfo loginInfo) {
        //重新设置搜索条件
        boolean search = this.setSearch(noticeSearch,loginInfo);
        if (!search) {
            throw new BaseException("搜索时间区间超过了允许搜索的时间段");
        }
//        log.info("queryByPage------------->noticeSearch:{}", JSON.toJSONString(noticeSearch));
        return this.noticeDao.queryAllByLimit(noticeSearch,
                Objects.nonNull(noticeSearch.getPageNum()) && Objects.nonNull(noticeSearch.getPageSize()) ? (noticeSearch.getPageNum() - 1) * noticeSearch.getPageSize() : null, noticeSearch.getPageSize());
    }


    /**
     * 查询总数
     *
     * @param noticeSearch 筛选条件
     * @return 查询结果
     */
    @Override
    public Integer queryCount(NoticeSearch noticeSearch) {
        return this.noticeDao.count(noticeSearch);
    }

    /**
     * 新增数据（会判断null字段，null字段不插入）
     *
     * @param noticeVO 实例对象
     * @return 实例对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void insertCheckNull(NoticeVO noticeVO, LoginInfo loginInfo) {
        Notice notice = new Notice();
        BeanUtils.copyProperties(noticeVO,notice,"startTime","endTime");
        notice.setCreateAccountUserId(loginInfo.getAccountUserId());
        notice.setStartTime(DateTimeUtil.StringToDateTime(noticeVO.getStartTime()));
        notice.setEndTime(DateTimeUtil.StringToDateTime(noticeVO.getEndTime()));
        Date now = new Date();
        notice.setCreateTime(now);
        notice.setUpdateTime(now);
        this.noticeDao.insertCheckNull(notice);
        noticeVO.setId(notice.getId());
        noticeVO.getAppointUserTypeList().forEach(noticeAppointType -> {
            noticeAppointType.setNoticeId(notice.getId());
        });
        //添加公告指定用户类型
        List<NoticeAppointType> appointUserTypeList = noticeVO.getAppointUserTypeList();
        //根据的当前登录用户的不同，如果选择了运维端选择了指定人群---->全部，那“全部”对应的角色也不同
        List<NoticeAppointType> allNoticeAppointTypes = this.getAllNoticeAppointTypes(appointUserTypeList, loginInfo);
        noticeAppointTypeDao.insertBatch(allNoticeAppointTypes);
        //添加公告指定生效运营区域
        this.insertNoticeAppointAccount(noticeVO);
        //添加公告指定生效平台
        this.insertNoticeAppointPlatform(noticeVO.getPlatformList(), notice.getId());
    }

    /**
     * 更新数据（会判断null字段，null字段不更新）
     *
     * @param noticeVO 实例对象
     * @return 实例对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateCheckNull(NoticeVO noticeVO) {
        Notice notice = new Notice();
        BeanUtils.copyProperties(noticeVO,notice,"startTime","endTime");
        notice.setStartTime(DateTimeUtil.StringToDateTime(noticeVO.getStartTime()));
        notice.setEndTime(DateTimeUtil.StringToDateTime(noticeVO.getEndTime()));
        Date now = new Date();
        notice.setUpdateTime(now);
        this.noticeDao.updateCheckNull(notice);
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteById(Integer id) {
        noticeAppointPlatformDao.deleteByNoticeId(id);
        noticeAppointAccountDao.deleteByNoticeId(id);
        noticeAppointTypeDao.deleteByNoticeId(id);
        return this.noticeDao.deleteById(id) > 0;
    }

    /**
     * @description: 获取用户公告列表
     * @author: mo.shanyong
     * @date: 2025/2/19 17:16
     * @param: pageNum 页码
     * @param: pageSize 页长
     * @param: loginInfo 登录用户
     * @param: loginInfo 平台：1、客户端；2、管理端；3、运维端
     * @return: java.util.List<com.tbit.uqbike.object.pojo.Notice>
     */
    @Override
    public List<Notice> getUserNoticeList(Integer pageNum, Integer pageSize, LoginInfo loginInfo,Integer platform) {
        //超管没有自己的公告列表
        if (loginInfo.roleIsAdmin()){
            return new ArrayList<>();
        }
        //获取当前用户所有的区域Id
        List<Integer> userAllAccountIdList = this.getUserAllAccountId(loginInfo);
        //设置了全部区域的公告也要能查询到
        userAllAccountIdList.add(0);
        threadLocalUserAccountIdList.set(userAllAccountIdList);
        List<Role> roles = roleDao.selectRolePermissionByUserId(loginInfo.getAccountUserId());
        List<Integer> roleIdList = roles.stream().map(Role::getRoleId).collect(Collectors.toList());
        threadLocalRoleIdList.set(roleIdList);
        //标志用户已读公告的日志类型是点击PV日志
        return noticeDao.getUserNoticeList(roleIdList, loginInfo.getPhone(),
                CommonConstant.NOTICE_LOG_TYPE_READ_PV, loginInfo.getAccountUserId(), platform,userAllAccountIdList,
                Objects.nonNull(pageNum) && Objects.nonNull(pageSize) ? (pageNum - 1) * pageSize : null, pageSize);
    }
    /**
     * @description: 查询用户的公告总数
     * @author: mo.shanyong
     * @date: 2025/2/19 18:08
     * @param: loginInfo
     * @param: platform 平台：1、客户端；2、管理端；3、运维端
     * @return: int
     */
    @Override
    public int getUserNoticeCount(LoginInfo loginInfo,Integer platform) {
        /*List<Role> roles = roleDao.selectRolePermissionByUserId(loginInfo.getAccountUserId());
        List<Integer> roleIdList = roles.stream().map(Role::getRoleId).collect(Collectors.toList());*/
        //超管没有自己的公告列表
        if (loginInfo.roleIsAdmin()){
            return 0;
        }
        List<Integer> roleIdList = threadLocalRoleIdList.get();
        List<Integer> userAccountIdList = threadLocalUserAccountIdList.get();
        return noticeDao.getUserNoticeCount(roleIdList,userAccountIdList, loginInfo.getPhone(),platform);
    }
    /**
     * @description: 获取用户未读总数
     * @author: mo.shanyong
     * @date: 2025/2/24 20:23
     * @param: platform 平台：1、客户端；2、管理端；3、运维端
     * @param: loginInfo
     * @return: int
     */
    @Override
    public int getUserNotReadNoticeCount(Integer platform, LoginInfo loginInfo) {
        if (loginInfo.roleIsAdmin()){
            return 0;
        }
        List<Integer> roleIdList = threadLocalRoleIdList.get();
        List<Integer> userAccountIdList = threadLocalUserAccountIdList.get();
        Integer userNotReadNoticeCount = noticeDao.getUserNotReadNoticeCount(roleIdList, loginInfo.getPhone(),
                CommonConstant.NOTICE_LOG_TYPE_READ_PV, loginInfo.getAccountUserId(), platform,userAccountIdList);
        userNotReadNoticeCount = Objects.isNull(userNotReadNoticeCount) ? 0 : userNotReadNoticeCount;
        threadLocalRoleIdList.remove();
        threadLocalUserAccountIdList.remove();
        return userNotReadNoticeCount;
    }

    /**
     * @description: 记录对公告的操作日志
     * @author: mo.shanyong
     * @date: 2025/2/20 15:52
     * @param: noticeId 公告id
     * @param: operateType 操作类型：1、曝光；3、点击；5、阅读
     * @param: loginInfo
     * @param: platform 平台：1、客户端；2、管理端；3、运维端
     * @return: java.lang.Integer
     */
    @Override
    public Integer recordNoticeLog(Integer noticeId, Integer operateType, LoginInfo loginInfo,Integer platform) {
        //记录日志
        Date now = new Date();
        Integer logId = this.addNoticeLog(noticeId, operateType, loginInfo, now, platform);
        return logId;
    }

    /**
     * @description: 重新设置搜索条件，并判断是否应该能查到数据。
     * @author: mo.shanyong
     * @date: 2025/2/18 19:39
     * @param: noticeSearch
     * @return: boolean
     */
    private boolean setSearch(NoticeSearch noticeSearch,LoginInfo info){
        //默认查询本月的数据
        String nowTime = DateTimeUtil.getNowTime();
        LocalDateTime beforeSixMonthTime = DateTimeUtil.getBeforeDateNumMonth(nowTime, 6);
        if (Objects.isNull(noticeSearch.getStartTime())){
            noticeSearch.setStartTime(DateTimeUtil.StringToDateTime(DateTimeUtil.getMonthFirstDateByDate(nowTime)));
        }else {//最多可以查从现在往前6个月的
            LocalDateTime startDateTime = DateTimeUtil.getLocalDateTimeByTimeStr(DateTimeUtil.geTime(noticeSearch.getStartTime()));
            noticeSearch.setStartTime(DateTimeUtil.StringToDateTime(DateTimeUtil.getDateStrByLocalDateTime(beforeSixMonthTime.isBefore(startDateTime) ? startDateTime : beforeSixMonthTime)));
        }
        if (Objects.isNull(noticeSearch.getEndTime())){
            noticeSearch.setEndTime(DateTimeUtil.StringToDateTime(nowTime));
        }else {
            LocalDateTime endDateTime = DateTimeUtil.getLocalDateTimeByTimeStr(DateTimeUtil.geTime(noticeSearch.getEndTime()));
            noticeSearch.setEndTime(DateTimeUtil.StringToDateTime(DateTimeUtil.getDateStrByLocalDateTime(beforeSixMonthTime.isBefore(endDateTime) ? endDateTime : beforeSixMonthTime)));
            return beforeSixMonthTime.isBefore(endDateTime);
        }
        //如果要根据生效平台查询，那应该加上全部的(0)
        if (CollectionUtils.isNotEmpty(noticeSearch.getPlatformList())){
            noticeSearch.getPlatformList().add(0);
        }
        //设置了全部区域的公告也要能找到
        if (CollectionUtils.isNotEmpty(noticeSearch.getAccountIdList())){
            noticeSearch.getAccountIdList().add(0);
        }
        //超级管理员不用管品牌权限，可以获取全部
        if (info.roleIsAdmin()){
            return true;
        }
        //当前登录账号是否绑定区域
        List<Account> accountList = holderAccountBoundService.getAccountByUserId(info.getAccountUserId());
        if (CollectionUtil.isNotEmpty(accountList)){
            noticeSearch.setCreateUserAccountIdList(accountList.stream().map(Account::getAccountId).collect(Collectors.toList()));
            return true;
        }
        //获取能查看的所有的账户id
        noticeSearch.setCreateUserAccountIdList(this.getAllAccountId(info.getBrandId(), info));
        return true;
    }
    /**
     * @description: 插入公告指定生效运营区域
     * @author: mo.shanyong
     * @date: 2025/2/19 15:05
     * @param: noticeVO
     */
    public void insertNoticeAppointAccount(NoticeVO noticeVO){
        //全部
        if (noticeVO.getAccountIdList().size() == 1 && noticeVO.getAccountIdList().get(0) == 0){
//            List<Account> accountList = accountDao.getAllAccount(3);//查询所有区域
//            List<Integer> accountIdList = accountList.stream().map(Account::getAccountId).collect(Collectors.toList());
            this.insertNoticeAccountIds(noticeVO.getAccountIdList(),noticeVO.getId());
            return;
        }
        List<Account> accountList = accountDao.getByAccountIds(noticeVO.getAccountIdList());
        Map<Integer, List<Account>> accountMap = accountList.stream().collect(Collectors.groupingBy(Account::getType));
        List<Integer> accountIdList = this.getSaveAccountId(accountMap, noticeVO.getAccountIdList(), 3);
        //找到全部的区域id
        accountIdList = accountDao.getAllAccountIdByIds(accountIdList);
        this.insertNoticeAccountIds(accountIdList,noticeVO.getId());
    }
    /**
     * @description: 排除掉代表相同区域的账户id，指定了区域，则排除区域对应的加盟商，指定了加盟商则排除掉对应的品牌
     * @author: mo.shanyong
     * @date: 2025/2/19 15:40
     * @param: accountMap 账户map
     * @param: accountIdList 全部的账户id
     * @param: type 账号类型，1--品牌；2--加盟商；3---区域
     * @return: java.util.List<java.lang.Integer>
     */
    private List<Integer> getSaveAccountId(Map<Integer, List<Account>> accountMap,List<Integer> accountIdList,int type){
        if (type <= 1){//计算到品牌时，返回数据
            return accountIdList;
        }
        List<Account> accounts = accountMap.get(type);
        if (CollectionUtils.isEmpty(accounts)){
            return getSaveAccountId(accountMap,accountIdList,--type);
        }
        List<Integer> collect = accounts.stream().map(Account::getParentId).collect(Collectors.toList());
        accountIdList = accountIdList.stream().filter(id ->{
            return !collect.contains(id);
        }).collect(Collectors.toList());
        if (type <= 2){//计算完加盟商后已经排除了存在的加盟商对应的品牌了，可以直接返回
            return accountIdList;
        }
        return getSaveAccountId(accountMap,accountIdList,--type);
    }

    /**
     * @description: 添加公告指定生效平台
     * @author: mo.shanyong
     * @date: 2025/2/19 15:47
     * @param: platformList 指定生效平台id
     * @param: noticeId 公告id
     */
    private void insertNoticeAppointPlatform(List<Integer> platformList,Integer noticeId){
        List<NoticeAppointPlatform> noticeAppointPlatformList = new ArrayList<>();
        for (Integer platform : platformList) {
            NoticeAppointPlatform noticeAppointPlatform = new NoticeAppointPlatform();
            noticeAppointPlatform.setPlatform(platform);
            noticeAppointPlatform.setNoticeId(noticeId);
            noticeAppointPlatformList.add(noticeAppointPlatform);
        }
        noticeAppointPlatformDao.insertBatch(noticeAppointPlatformList);
    }


    /**
     * @description: 记录对公告的操作日志
     * @author: mo.shanyong
     * @date: 2025/2/20 16:11
     * @param: noticeReadLogs 查询是否存在操作日志
     * @param: noticeId 公告id
     * @param: logType 操作类型：1、曝光；3、点击；5、阅读
     * @param: loginInfo 用户
     */
    private Integer addNoticeLog(Integer noticeId, Integer logType, LoginInfo loginInfo,Date now,Integer platform){
        //PV的数据量一定要记录
        NoticeReadLog noticeReadLogPV = new NoticeReadLog();
        noticeReadLogPV.setLogType(logType);
        noticeReadLogPV.setCreateTime(now);
        noticeReadLogPV.setUserId(loginInfo.getAccountUserId());
        noticeReadLogPV.setNoticeId(noticeId);
        noticeReadLogPV.setPlatform(platform);
        noticeReadLogDao.insertCheckNull(noticeReadLogPV);
        return noticeReadLogPV.getId();
    }

    /**
     * @description: 批量插入公告生效的区域id
     * @author: mo.shanyong
     * @date: 2025/2/21 16:34
     * @param: accountIdList
     * @param: noticeId 公告Id
     */
    private void insertNoticeAccountIds(List<Integer> accountIdList,Integer noticeId){
        List<NoticeAppointAccount> noticeAppointAccountList = new ArrayList<>();
        for (Integer accountId : accountIdList) {
            NoticeAppointAccount noticeAppointAccount = new NoticeAppointAccount();
            noticeAppointAccount.setAccountId(accountId);
            noticeAppointAccount.setNoticeId(noticeId);
            noticeAppointAccountList.add(noticeAppointAccount);
        }
        noticeAppointAccountDao.insertBatch(noticeAppointAccountList);
    }

    /**
     * @description: 根据品牌id获取所有的区域的、加盟商的id
     * @author: mo.shanyong
     * @date: 2025/2/24 17:25
     * @param: brandId 品牌id
     * @return: java.util.List<java.lang.Integer>
     */
    private List<Integer> getAllAccountId(Integer brandId,LoginInfo info) {
        if (UserConstant.USER_BRAND.equals(info.type)) {
            //获取全部的id(品牌id、加盟商id、区域id)
            return accountDao.getAllId(brandId);
        } else if (UserConstant.USER_FRANCHISEE.equals(info.type)) {//加盟商账户可以获取当前加盟商和以下区域
            //加盟商Id
            List<Integer> allIdList = new ArrayList<>(Collections.singletonList(info.getId()));
            //区域id
            List<Integer> accountIdList = accountDao.getAllAccountIdByIds(allIdList);
            allIdList.addAll(accountIdList);
            return allIdList;
        }
        //区域用户只能获取当前区域
        return Collections.singletonList(info.getId());
    }

    /**
     * @description: 获取这个用户所在的所有区域id
     * @author: mo.shanyong
     * @date: 2025/2/24 17:25
     * @param: brandId 品牌id
     * @return: java.util.List<java.lang.Integer>
     */
    private List<Integer> getUserAllAccountId(LoginInfo info){
        //当前登录账号是否绑定区域
        List<Account> accountList = holderAccountBoundService.getAccountByUserId(info.getAccountUserId());
        if (CollectionUtil.isNotEmpty(accountList)){
            return accountList.stream().map(Account::getAccountId).collect(Collectors.toList());
        }
        List<Integer> accountIdList = new ArrayList<>();
        //品牌能获取这个品牌下所有的区域，
        if (UserConstant.USER_BRAND.equals(info.type)) {
            //获取全部的区域
            List<Account> accounts = accountDao.getById(info.getBrandId());
            if (CollectionUtils.isNotEmpty(accounts)){
                return accounts.stream().map(Account::getAccountId).collect(Collectors.toList());
            }
            return accountIdList;
        }else if (UserConstant.USER_FRANCHISEE.equals(info.type)){//加盟商能获取当前加盟商下的区域
            List<Account> accounts = accountDao.listByParentId(info.getId());
            if (CollectionUtils.isNotEmpty(accounts)){
                return accounts.stream().map(Account::getAccountId).collect(Collectors.toList());
            }
            return accountIdList;
        }
        //区域用户只能获取所有的区域用户
        Account account = accountDao.getByAccountId(info.getId());
        if (Objects.isNull(account)){
            log.info("用户所在的运营区为空，LoginInfo.id:{}",info.getId());
            return accountIdList;
        }
        accountIdList.add(account.getAccountId());
        return accountIdList;
    }

    /**
     * @description: 根据当前用户获取运维端指定人群--->“全部”代表的角色
     * @author: mo.shanyong
     * @date: 2025/3/6 9:29
     * @param: noticeAppointTypeList 接收人指定
     * @param: info 登录用户
     * @return: java.util.List<com.tbit.uqbike.object.pojo.NoticeAppointType>
     */
    private List<NoticeAppointType> getAllNoticeAppointTypes(List<NoticeAppointType> noticeAppointTypeList,LoginInfo info){
        if (info.roleIsAdmin()){//超级管理员不做处理
            return noticeAppointTypeList;
        }
        NoticeAppointType mainNoticeAppointType = noticeAppointTypeList.stream().filter(noticeAppointType -> {
            return Objects.equals(noticeAppointType.getPlatform(), CommonConstant.NOTICE_MANAGER_PLATFORM)
                    && Objects.equals(noticeAppointType.getType(), -1);//运维端选择了全部
        }).findFirst().orElse(null);
        if (Objects.isNull(mainNoticeAppointType)){//如果没选运维端全部
            return noticeAppointTypeList;
        }
        //品牌、加盟商、区域等级的用户，要根据等级获取该等级能获取到的全部角色
        List<Role> roleList = roleService.getRoleListByUser(info);
        List<NoticeAppointType> noticeAppointTypes = noticeAppointTypeList.stream().filter(noticeAppointType -> {
            return !(Objects.equals(noticeAppointType.getPlatform(), CommonConstant.NOTICE_MANAGER_PLATFORM)
                    && Objects.equals(noticeAppointType.getType(), -1));
        }).collect(Collectors.toList());
        for (Role role : roleList) {
            NoticeAppointType noticeAppointType = new NoticeAppointType();
            BeanUtils.copyProperties(mainNoticeAppointType,noticeAppointType);
            noticeAppointType.setType(role.getRoleId());
            noticeAppointTypes.add(noticeAppointType);
        }
        return noticeAppointTypes;
    }

}
